package reporte;

import java.util.*;
import org.joda.time.DateTime;
import clientes.Cliente;
import clientes.Ticket;
import clientes.TicketCcProductos;
import envios.Envio;
import exceptions.ParametrosMalColocadosException;
import productosYPresentaciones.Producto;
import ventas.Venta;
import ventas.VentaConEnvio;
import reporte.filtros.*;
import reporte.BuilderFiltroVenta;
import sucursal.Sucursal;

/**
 * Esta clase representa a un reporte que realiza la 
 * cadena de sucursales sobre una sucursal sobre consultas de la misma.
 * 
 * @author Octavio Nicolas Pompilio.
 *
 */

public class Reporte {

	private Sucursal sucursal;
	
	/**
	 * Construye un reporte que utilizara como referencia una sucursal.
	 * @param sucursal
	 * La sucursal sobre la que se realiza el reporte.
	 */
	public Reporte(Sucursal sucursal) {
		this.sucursal = sucursal;
	}
	
	/**
	 * Denota la sucursal sobre la cual se realizara el reporte.
	 * @return
	 * La sucursal a la que consultar.
	 */
	public Sucursal getSucursal() {
		return this.sucursal;
	}
	
	/**
	 * Realiza una consulta sobre una lista de ventas aplicando
	 * el filtro.
	 * @param filtro
	 * El filtro de ventas que se utilizara.
	 * @param coleccionVentas
	 * La lista de ventas a filtrar.
	 * @return
	 * El resultado del filtro sobre esa coleccion.
	 */
	public List<Venta> aplicarFiltroSobreListaVentas(Filtro<Venta> filtro, List<Venta> coleccionVentas){
		return filtro.filtrar(coleccionVentas);
	}
	
	/**
	 * Consulta cuales son las ventas realizadas en la sucursal.
	 * @return
	 * Una lista de ventas.
	 */
	public List<Venta> ventasRealizadas(){
		return this.getSucursal().getVentasRealizadas();
	}
	
	/**
	 * Consulta cuales son las ventas que fueron realizadas 
	 * entre dos fechas.
	 * @param fechaI
	 * Fecha inicial del intervalo.
	 * @param fechaF
	 * Fecha final del intervalo.
	 * @return
	 * Una lista de ventas entre ambas fechas.
	 * @throws ParametrosMalColocadosException 
	 * Se lanza cuando estan mal colocados los parametros.
	 */
	public List<Venta> ventasEntreFechas(DateTime fechaI,DateTime fechaF) throws ParametrosMalColocadosException{
		return this.aplicarFiltroSobreListaVentas(
					BuilderFiltroVenta.filtroEntreFechas(fechaI,fechaF).build(),
					this.ventasRealizadas());
	}

	/**
	 * Consulta cuales son las ventas que cuyo pago se encuentra entre
	 * dos montos.
	 * @param montoI
	 * Monto inicial del intervalo.
	 * @param montoF
	 * Fecha final del intervalo.
	 * @return
	 * Una lista de ventas entre ambos montos.
	 * @throws ParametrosMalColocadosException 
	 * Se lanza cuando estan mal colocados los parametros.
	 */
	public List<Venta> ventasEntreMontos(float montoI,float montoF) throws ParametrosMalColocadosException{
		return this.aplicarFiltroSobreListaVentas(
				BuilderFiltroVenta.filtroEntreMontos(montoI,montoF).build(),
				this.ventasRealizadas());
	}
	
	/**
	 * Consulta cuales son las ventas que tienen a un producto en particular.
	 * @param producto
	 * El producto que se usa como referencia de la consulta.
	 * @return
	 * Una lista de ventas en la que se encuentra el producto.
	 */
	public List<Venta> ventasConProducto(Producto producto){
		return this.aplicarFiltroSobreListaVentas(
				BuilderFiltroVenta.filtroPorProducto(producto).build(),
				this.ventasRealizadas());
	}
	
	/**
	 * Consulta cuales son los detalles de compra de los clientes.
	 * @return
	 * Una lista de detalles de compra asociadas a su respectivo cliente.
	 */
	public Map<Cliente,List<Ticket> > cuentaCcPesos(){
		Map<Cliente,List<Ticket>> listaCcPesos = 
					new HashMap<Cliente,List<Ticket> >();
		
		for(Cliente cliente:this.getSucursal().getCadena().getClientes() ){
			listaCcPesos.put(cliente,cliente.getTickets() );
		}
		
		return listaCcPesos;
	}

	/**
	 * Consulta cuales son los detalles de retiro/entrega 
	 * de los clientes.
	 * @return
	 * Una lista de detalles de retiro/entrega asociadas a 
	 * su respectivo cliente.
	 */
	public Map<Cliente,List<Ticket>> cuentaCcProductos(){
		Map<Cliente, List<Ticket>> listaCcProductos = 
				new HashMap<Cliente, List<Ticket>>();
	
		for(Cliente cliente:this.getSucursal().getCadena().getClientes() ){
			listaCcProductos.put(cliente,cliente.getTickets() );
		}
		
		return listaCcProductos;
	}
	
	
	/**
	 * Realiza una consulta sobre una lista de productos aplicando
	 * el filtro.
	 * @param filtro
	 * El filtro de productos que se utilizara.
	 * @param coleccionVentas
	 * La lista de productos a filtrar.
	 * @return
	 * El resultado del filtro sobre esa coleccion.
	 */
	public List<Producto> aplicarFiltroSobreListaProductos(Filtro<Producto> filtro, List<Producto> coleccionProductos){
		return filtro.filtrar(coleccionProductos);
	}
	
	/**
	 * Consulta los productos que tienen al stock por debajo del stock minimo.
	 * @return
	 * Una lista de productos.
	 */
	public List<Producto> productosPorDebajoDeStockMin(){
		return this.aplicarFiltroSobreListaProductos(
				BuilderFiltroProducto.filtroPorDebajoDeStockMinimo
					(this.getSucursal().getStockProductos()).build(),
				this.getSucursal().getStockProductos().getProductosDelStock());
	}
	
	/**
	 * Consulta los productos que tienen al stock por debajo del stock critico
	 * @return
	 * Una lista de productos.
	 */
	public List<Producto> productosPorDebajoDeStockCritico(){
		return this.aplicarFiltroSobreListaProductos(
				BuilderFiltroProducto.filtroPorDebajoDeStockCritico
					(this.getSucursal().getStockProductos()).build(),
				this.getSucursal().getStockProductos().getProductosDelStock());
	}
	
	/**
	 * Consulta cuales son los envios pendientes, ordenados por fecha.
	 * @return
	 * Denota una lista ordenada de envios pendientes.
	 */
	public List<Envio> enviospendientes(){
		return this.ordenarEnviosPorFecha( this.filtrarEnviosPendientes( 
				this.getEnvios() ) );
	}
	
	/**
	 * Mapea una lista de ventas a una de envios.
	 * @param ventas
	 * La lista que se transformara
	 * @return
	 * Una lista de envios.
	 */
	private List<Envio> getEnvios(List<Venta> ventas){
		List<Envio> listaEnvios = new ArrayList<Envio>();
		for(Venta venta:ventas){
			listaEnvios.add( ((VentaConEnvio) venta).getEnvioAProcesar() );
		}
		
		return listaEnvios;
	}

	/**
	 * Mapea una lista de ventas a una de envios.
	 * @param ventas
	 * La lista que se transformara
	 * @return
	 * Una lista de envios.
	 */
	public List<Envio> getEnvios(){
		return this.getEnvios( this.aplicarFiltroSobreListaVentas( BuilderFiltroVenta.filtroConEnvios().build(),
				this.ventasRealizadas()) );
	}
	
	/**
	 * Filtra los envios pendientes en la sucursal en cuestion.
	 * @param envios
	 * La lista de productos sobre la cual se aplicara la consulta
	 * @return
	 * Lista de envios pendientes.
	 */
	private List<Envio> filtrarEnviosPendientes(List<Envio> envios){
		List<Envio> listaEnvios = new ArrayList<Envio>();
		for(Envio envio:envios){
			if( envio.estaPendienteDeEnvio() )
				listaEnvios.add( envio );
		}
		
		return listaEnvios;
	}
	
	/**
	 * Ordena los envios por fecha, en orden ascendente.
	 * @param listaEnvios
	 * La lista de envios que se ordenara.
	 * @return
	 * Una lista ordenada de envios.
	 */
	//Algoritmo de ordenacion burbuja
	private List<Envio> ordenarEnviosPorFecha(List<Envio> listaEnvios){		
		List<Envio> enviosOrdenados = new ArrayList<Envio>();
		
		while( !listaEnvios.isEmpty() ){
			//Obtengo el primer envio
			Envio envioConMenorFechaEnvio = listaEnvios.get(0);
			//Obtengo el minimo
			for(Envio envio:listaEnvios){
				if( envio.getFecha().isBefore( envioConMenorFechaEnvio.getFecha() ) )
					envioConMenorFechaEnvio = envio;
			}
			//Lo saco de la lista y lo agrego en la otra
			listaEnvios.remove(envioConMenorFechaEnvio);
			enviosOrdenados.add(envioConMenorFechaEnvio);
		}
		
		return enviosOrdenados;
	}
}